/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
 * (c) 2013 OpenPlans
 * This code is licensed under the GPL 2.0 license, available at the root
 * application directory.
 */
package org.geoserver.kml.iterator;

import de.micromata.opengis.kml.v_2_2_0.Feature;
import de.micromata.opengis.kml.v_2_2_0.Folder;
import java.util.Iterator;
import java.util.List;
import org.geoserver.kml.KmlEncodingContext;
import org.geoserver.kml.decorator.KmlDecoratorFactory.KmlDecorator;
import org.geoserver.kml.utils.KMLFeatureAccessor;
import org.geoserver.ows.HttpErrorCodeException;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.WMSMapContent;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.map.FeatureLayer;
import org.geotools.map.Layer;

/**
 * Base class for iterators that create one folder per layer
 *
 * @author Andrea Aime - GeoSolutions
 */
public abstract class AbstractFolderIteratorFactory implements IteratorFactory<Feature> {

    protected KmlEncodingContext context;

    protected List<KmlDecorator> decorators;

    public AbstractFolderIteratorFactory(KmlEncodingContext context) {
        this.context = context;
        this.decorators = context.getDecoratorsForClass(Folder.class);
    }

    public abstract class AbstractFolderGenerator implements Iterator<Feature> {
        Iterator layerIterator;

        public AbstractFolderGenerator() {
            this.layerIterator = context.getMapContent().layers().iterator();
        }

        @Override
        public Feature next() {
            while (hasNext()) {
                Layer layer = (Layer) layerIterator.next();
                context.setCurrentLayer(layer);

                // setup the folder and let it be decorated
                Folder folder = new Folder();
                folder.setName(layer.getTitle());

                // if it's a feature layer, setup the feature collection for it (some decorators use
                // it)
                if (layer instanceof FeatureLayer) {
                    try {
                        WMSMapContent mapContent = context.getMapContent();
                        SimpleFeatureCollection fc =
                                new KMLFeatureAccessor()
                                        .loadFeatureCollection(
                                                layer,
                                                mapContent,
                                                context.getWms(),
                                                mapContent.getScaleDenominator());
                        context.setCurrentFeatureCollection(fc);
                    } catch (Exception e) {
                        if (e instanceof ServiceException) {
                            throw (ServiceException) e;
                        } else if (e instanceof HttpErrorCodeException) {
                            throw (HttpErrorCodeException) e;
                        } else {
                            throw new ServiceException(
                                    "Failed to load vector data during KML generation", e);
                        }
                    }
                }

                // have the folder be decorated
                for (KmlDecorator decorator : decorators) {
                    folder = (Folder) decorator.decorate(folder, context);
                    if (folder == null) {
                        continue;
                    }
                }

                encodeFolderContents(layer, folder);

                return folder;
            }
            return null;
        }

        @Override
        public boolean hasNext() {
            return layerIterator.hasNext();
        }

        /** Encodes the actual contents of the folder, besides the one generated by decorators */
        protected abstract void encodeFolderContents(Layer layer, Folder folder);
    }
}
